package com.mystnihon.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.widget.ImageView;

public class RotaryKnobView extends ImageView {

	private static final float DEFAULT_THRESHOLD = 360f / 10f;
	private float notificationThreshold = DEFAULT_THRESHOLD;
	private float angle = 0f;
	private float theta_old = 0f;
	float delta_theta = 0;
	float width = 0;
	float height = 0;
	float lastnotiftheta = 0f;

	private RotaryKnobListener listener;

	public interface RotaryKnobListener {
		public void onKnobChanged(int direction);
	}

	public void setKnobListener(RotaryKnobListener l) {
		listener = l;
	}

	public RotaryKnobView(Context context) {
		super(context);
	}

	public RotaryKnobView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public RotaryKnobView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	final float input[] = new float[1];
	final float output[] = new float[1];

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		int action = event.getAction();
		int actionCode = action & MotionEvent.ACTION_MASK;
		if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
			attemptClaimDrag();
			float x = event.getX(0);
			float y = event.getY(0);
			theta_old = getTheta(x, y);
		} else if (actionCode == MotionEvent.ACTION_MOVE) {
			attemptClaimDrag();
			float x = event.getX(0);
			float y = event.getY(0);

			float theta = getTheta(x, y);

			// We increase the angle to have a delta the more precise
			// possible.
			delta_theta = theta * 1000 - lastnotiftheta * 1000;
			// Log.i(VIEW_LOG_TAG, String.format("DeltaTheta %f ",
			// delta_theta));
			if (theta < 353 && theta > 7) {
				angle = angle + (theta - theta_old);

				theta_old = theta;

				int direction = (delta_theta > 0) ? 1 : -1;

				// Log.i(VIEW_LOG_TAG, String.format("direction %d ",
				// direction));
				if (direction == -1) {
					float seuil = lastnotiftheta - getNotificationThreshold();

					if (seuil < 0) {
						seuil += 360;
					}

					if (seuil > theta) {
						Log.i(VIEW_LOG_TAG, String.format("[Direction -1] Seuil %f | Theta %f ", seuil, theta));
						lastnotiftheta = theta;
						notifyListener(direction);
					}
				} else if (direction == 1) {
					float seuil = lastnotiftheta + getNotificationThreshold();
					if (seuil > 360) {
						seuil -= 360;
					}

					if (seuil < theta) {
						Log.i(VIEW_LOG_TAG, String.format("[Direction +1] Seuil %f | Theta %f ", seuil, theta));
						lastnotiftheta = theta;
						notifyListener(direction);
					}
				}
				invalidate();

			}
		}
		return true;
	}

	/*
	 * time smoothing constant for low-pass filter 0 ≤ alpha ≤ 1 ; a smaller
	 * value basically means more smoothing See:
	 * http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
	 */
	static final float ALPHA = 0.15f;

	/**
	 * @see http 
	 *      ://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
	 * @see http 
	 *      ://developer.android.com/reference/android/hardware/SensorEvent.html
	 *      #values
	 */
	protected float[] lowPass(float[] input, float[] output) {
		if (output == null)
			return input;

		for (int i = 0; i < input.length; i++) {
			output[i] = output[i] + ALPHA * (input[i] - output[i]);
		}
		return output;
	}

	private float getTheta(float x, float y) {
		if (width <= 0) {
			width = getWidth();
		}
		if (height <= 0) {
			height = getWidth();
		}

		// Changement de répère pour un ayant son origine au centre de la vue.
		float sx = x - (width / 2.0f);
		float sy = y - (height / 2.0f);

		float length = (float) FloatMath.sqrt(sx * sx + sy * sy);
		float nx = sx / length;
		float ny = sy / length;

		float theta = (float) Math.atan2(ny, nx);

		final float rad2deg = (float) (180.0 / Math.PI);
		float theta2 = (theta + (float) Math.PI / 2) * rad2deg;

		return (theta2 < 0) ? theta2 + 360.0f : theta2;
	}

	/**
	 * Tries to claim the user's drag motion, and requests disallowing any
	 * ancestors from stealing events in the drag.
	 */
	private void attemptClaimDrag() {
		if (getParent() != null) {
			getParent().requestDisallowInterceptTouchEvent(true);
		}
	}

	private void notifyListener(int direction) {
		if (null != listener) {
			listener.onKnobChanged(direction);
			if (direction > 0) {
				playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
			} else if (direction < 0) {
				playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
			}
		}
	}

	protected void onDraw(Canvas c) {
		c.rotate(angle, getWidth() / 2, getHeight() / 2);
		super.onDraw(c);
	}

	public float getNotificationThreshold() {
		return notificationThreshold;
	}

	public void setNotificationThreshold(float threshold) {
		notificationThreshold = threshold;
	}
}